package com.liuhouse.filter;

import com.alibaba.fastjson.JSON;
import com.liuhouse.domain.LoginUser;
import com.liuhouse.enums.AppHttpCodeEnum;
import com.liuhouse.utils.JwtUtil;
import com.liuhouse.utils.RedisCache;
import com.liuhouse.utils.ResponseResult;
import com.liuhouse.utils.WebUtils;
import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

@Component
//博客前台的登录认证过滤器 ， OncePerRequestFilter是springsecurity提供的类
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


    @Resource
    //RedisCache是我们在liuhouse-framework工程中写的工具类,用于操作redis
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        /**
         * 其实很多东西都是框架定好的  人家框架需要什么数据 咱只需要熟能生巧的给什么数据就行了  不必纠结   达到目的即可
         * 分析
         * 获取token，由于我们把token存入Redis的时候加了前缀,所以在获取的时候要注意前缀
         * 解析token获取其中的userid
         * 从redis中获取用户信息
         * 存入SecurityContextHolder
         */
        //获取请求头中的token
        String token = request.getHeader("token");
        //判断上面那样有没有获取到token值
        if (!StringUtils.hasText(token)) {
            //说明该接口不需要登录,直接放行,不拦截
            filterChain.doFilter(request, response);
            return;
        }
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            //当token过期或者token被篡改就会进入下面那行的异常处理
            e.printStackTrace();
            //报了异常之后,把异常响应给前端,需要重新登录 ResponseResult AppHttpCodeEnum WebUtils 是我们在liuhouse-framework工程中写的
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(response, JSON.toJSONString(result));
            return;
        }
        //获取token解析后的 sub   也就是用户id
        String userid = claims.getSubject();
        //在redis中,通过key来获取值,注意key是我们加过前缀的,取值的时候也要加上前缀
        LoginUser loginUser = redisCache.getCacheObject("login:" + userid);
        //如果redis中没有取到值,说明是登录过期了,需要重新登录
        if (Objects.isNull(loginUser)) {
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(response, JSON.toJSONString(result));
            return;
        }
        //把redis中获取到的值,存入到SecurityContextHolder(Security官方提供的类),框架内部会使用,这是固定的字段
        //不用去想  照着去写就行了   先实现最终效果  完了之后再来补充知识  固定写法
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request, response);
    }
}
